package org.oimwrapper.connectors.common;

import Thor.API.*;
import Thor.API.Operations.*;

import java.lang.reflect.*;
import java.util.*;import org.oimwrapper.connectors.*;

/**
 * Retries open provisioning tasks which are assigned to a user.<p>
 *
 * This task expects the following parameters, in addition to the standard
 * parameters in the {@link OIMTaskBase} class.
 * <ul>
 * <li><code>ResourceName</code> The name of the resource object to search for tasks to retry.</li>
 * <li><code>TaskName</code> The name of a the task to retry.</li>
 * <li><code>OimUser</code> The name of the user in OIM to retry the open tasks for.  This will 
 *                          default to <i>XELSYSADM</i> if not specified.</li>
 * <li><code>ResourceType</code> The type of resource to retry tasks for.  This will default to
 *                               <i>Application</i> if not specified.</li>
 * <li><code>Threads</code> The number of concurrent threads to run to submit events to OIM.  By default, ten
 *                          submitting threads are run.</li>
 * </ul>
 */
public class OIMRetryProvisioningTasks extends OIMTaskBase {

	/**
	 * True if a stop has been requested for this task.
	 */
	private boolean stop;
	
	/**
	 * The provisioning operation utility used by this task.
	 */
	private tcProvisioningOperationsIntf provOp;

	/**
	 * Default constructor.
	 */
	public OIMRetryProvisioningTasks() {
		super("OIMWRAPPER.TASK");
		stop = false;
	}

	/**
	 * This method is called by the scheduler to execute the task instance.
	 */
	public void execute() {
		try {
			String resourceName = getCriticalAttribute("ResourceName");
			String taskName = getCriticalAttribute("TaskName");
			String oimUsername = getDefaultAttribute("OimUser", "XELSYSADM");
			String resourceType = getDefaultAttribute("ResourceType", "Application");
			provOp = (tcProvisioningOperationsIntf) getUtility(tcProvisioningOperationsIntf.class);
            if (resourceName.equalsIgnoreCase("*")) {
				tcObjectOperationsIntf objectOp = (tcObjectOperationsIntf) getUtility(tcObjectOperationsIntf.class);
				Map searchFor = new HashMap();
				searchFor.put("Objects.Object Type", resourceType);
				tcResultSet objects = objectOp.findObjects(searchFor);
				for (int i = 0; !stop && i < objects.getRowCount(); i++) {
					objects.goToRow(i);
					processResource(objects.getStringValue("Objects.Name"), taskName, oimUsername);
				}
			} else {
				processResource(resourceName, taskName, oimUsername);
			}
		} catch (Exception e) {
			OIMExceptionHandler.handleException(logger, e, getClass());
		}
	}
	
	/**
	 * This processes a specific resource type.
	 *
	 * @param ResourceName The name of the resource object to process.
	 * @param TaskName The name of the open task to process.
	 * @param OimUsername The name of the user to process the open tasks for.
	 * @exception Exception
	 */
	protected void processResource(String ResourceName, String TaskName, String OimUsername) throws Exception {
		Map searchFor = new HashMap();
		searchFor.put("Process Definition.Tasks.Task Name", TaskName);
		searchFor.put("Objects.Name", ResourceName);
		String[] status = new String[] { "Pending", "Rejected" };
		logger.info("Finding open provisioning tasks assigned to " + OimUsername + " for " + ResourceName + " objects");
		tcResultSet tasks = null;
		if (checkIfOimVersion("9.1")) {
			tasks = provOp.getAssignedOpenProvisioningTasks(getOimUser(OimUsername), searchFor, status);
		} else {
			Method getTasks = provOp.getClass().getMethod("getAssignedProvisioningTasks", new Class[] {long.class, Map.class, status.getClass()});
			tasks = (tcResultSet) getTasks.invoke(provOp, new Object[] {new Long(getOimUser(OimUsername)), searchFor, status});
		}
		logger.info("Found " + Integer.toString(tasks.getRowCount()) + " pending/rejected tasks to retry.");
		int maxWorkers = Integer.parseInt(getDefaultAttribute("Threads", "10"));
		for (int i = 0; !stop && i < tasks.getRowCount(); i++) {
			tasks.goToRow(i);
			logger.info("Retrying " + TaskName + " " + Integer.toString(i + 1) + " of " + Integer.toString(tasks.getRowCount()));
			long taskKey = tasks.getLongValue("Process Instance.Task Details.Key");
			startThread(maxWorkers, new worker(this, taskKey));
		}
		waitForAllThreads();
		logger.info("Finished retrying all pending/rejected instances of " + TaskName + " for " + ResourceName);
	}

	/**
	 * This method is called by the scheduler to stop a running task instance.
	 * 
	 * @return <code>true</code>
	 */
	public boolean stop() {
		logger.info(" ---- Stopping current task ---- ");
		stop = true;
		return stop;
	}
	
	/**
	 * Processes a single task.
	 * 
	 * @param TaskKey The task key of the task to process.
	 */
	private void processSingleTask(long TaskKey) {
		try {
			String result = provOp.retryTask(TaskKey);
			logger.info("Task Result = " + result);
		} catch (Exception e) {
			OIMExceptionHandler.handleException(logger, e, getClass());
		}
	}
	
	/**
	 * Worker Thread.
	 */
	private class worker extends OIMTaskThreadBase {
		
		/**
		 * The invoking task.
		 */
		private OIMRetryProvisioningTasks task;
		
		/**
		 * The record to process.
		 */
		private long taskKey;
		
		/**
		 * Default Constructor.
		 * 
		 * @param Task The invoking task.
		 * @param TaskKey The task key of the task to process.
		 */
		private worker(OIMRetryProvisioningTasks Task, long TaskKey) {
			super(Task);
			task = Task;
			taskKey = TaskKey;
		}
		
		/**
		 * Processes a single task.
		 */
		public void runOimThread() {
			task.processSingleTask(taskKey);
		}
	}
}